package com.petmarket.client;

import com.petmarket.common.AccountManger;
import com.petmarket.common.CommonClient;
import com.petmarket.common.PageResult;
import com.petmarket.contract.PetContract;
import com.petmarket.entity.Pet;
import com.petmarket.uitls.SpringUtils;
import org.fisco.bcos.sdk.jni.common.JniException;
import org.fisco.bcos.sdk.v3.BcosSDK;
import org.fisco.bcos.sdk.v3.client.Client;
import org.fisco.bcos.sdk.v3.codec.ContractCodec;
import org.fisco.bcos.sdk.v3.codec.ContractCodecException;
import org.fisco.bcos.sdk.v3.codec.datatypes.generated.tuples.generated.Tuple7;
import org.fisco.bcos.sdk.v3.crypto.signature.SignatureResult;
import org.fisco.bcos.sdk.v3.model.TransactionReceipt;
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.ProxySignTransactionManager;
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.dto.AbiEncodedRequest;
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.utils.TransactionRequestBuilder;
import org.fisco.bcos.sdk.v3.transaction.model.exception.ContractException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

@Service
public class PetClient extends CommonClient implements ApplicationRunner {
    public static final Logger logger = LoggerFactory.getLogger(PetClient.class.getName());

    public void insert(Pet pet) throws ContractException {
        PetContract petContract = (PetContract) getContractMap().get("PetMarket");
        BigInteger count = petContract.count(BigInteger.valueOf(0), BigInteger.valueOf(200)).add(BigInteger.valueOf(1));
        TransactionReceipt receipt = petContract.insert(count,pet.getName(),pet.getType(),pet.getSellerId(),pet.getPetPrice()
                ,pet.getStatus(),pet.getImgUrl(),"0");
        logger.info("调用PetClient的insert方法");
        logger.info("结果：{}", receipt);
    }

    public void insert(Pet pet,String hexPrivateKey) throws ContractException, ContractCodecException, JniException {
        BcosSDK sdk = SpringUtils.getBean("bcosSDK");
        // 为群组初始化client
        Client client = sdk.getClient();

        // 初始化ProxySignTransactionManager
        ProxySignTransactionManager proxySignTransactionManager = new ProxySignTransactionManager(client);

// ProxySignTransactionManager.setAsyncTransactionSigner接口可以修改AsyncTransactionSignercInterface的实现
        proxySignTransactionManager.setAsyncTransactionSigner((hash, transactionSignCallback) -> {
            SignatureResult sign = client.getCryptoSuite().sign(hash, AccountManger.loadAccount(hexPrivateKey));
            transactionSignCallback.handleSignedTransaction(sign);
        });

        PetContract petContract = (PetContract) getContractMap().get("PetMarket");
        BigInteger count = petContract.count(BigInteger.valueOf(0), BigInteger.valueOf(200)).add(BigInteger.valueOf(1));

        List<Object>params=new ArrayList<>();
        params.add(count);
        params.add(pet.getName());
        params.add(pet.getType());
        params.add(pet.getSellerId());
        params.add(pet.getPetPrice());
        params.add(pet.getStatus());
        params.add(pet.getImgUrl());
        params.add(pet.getIsDeleted());

        ContractCodec contractCodec=new ContractCodec(client.getCryptoSuite(),false);

// 对合约参数进行编解码
        byte[] abiEncoded = contractCodec.encodeMethod(PetContract.getABI(), "insert", params);

// 采用链式构造AbiEncodedRequest，传入contractAddress、nonce、blockLimit等重要参数，最后使用buildAbiEncodedRequest结束构造。
        AbiEncodedRequest request =
                new TransactionRequestBuilder()
                        .setTo(CommonClient.getContractAddress("PetMarket"))
                        .setAbi(PetContract.getABI())
                        .setBlockLimit(client.getBlockLimit())
                        .setExtension("Hello".getBytes())
                        .setGasPrice(BigInteger.TEN)
                        .buildAbiEncodedRequest(abiEncoded);

// 同步发送上链，获得回执
        TransactionReceipt receipt = proxySignTransactionManager.sendTransaction(request);

        logger.info("调用petClient的edit(重载)方法");
        logger.info("结果：{}", receipt);
    }

    public Pet query(Pet pet) throws ContractException {
        PetContract petContract = (PetContract) getContractMap().get("PetMarket");
        Tuple7<String, String, String, String, String, String, String> getValue = petContract.select(pet.getId());
        Pet resPet = new Pet();
        resPet.setId(pet.getId());
        resPet.setName(getValue.getValue1());
        resPet.setType(getValue.getValue2());
        resPet.setSellerId(getValue.getValue3());
        resPet.setPetPrice(getValue.getValue4());
        resPet.setStatus(getValue.getValue5());
        resPet.setImgUrl(getValue.getValue6());
        resPet.setIsDeleted(getValue.getValue7());
        if(resPet.getName().isEmpty() || resPet.getIsDeleted().equals("1")){
            throw new RuntimeException("执行失败");
        }
        logger.info("调用petClient的query方法");
        logger.info("结果：{}", resPet);
        return resPet;
    }

    public void edit(Pet pet){
        PetContract petContract = (PetContract) getContractMap().get("PetMarket");
        TransactionReceipt receipt = petContract.update(pet.getId(),pet.getName(),pet.getType(),pet.getSellerId(),pet.getPetPrice()
                ,pet.getStatus(),pet.getImgUrl());
        logger.info("调用petClient的edit方法");
        logger.info("结果：{}", receipt);
    }

    public void edit(Pet pet,String hexPrivateKey) throws JniException, ContractCodecException, ContractException {

        BcosSDK sdk = SpringUtils.getBean("bcosSDK");
        // 为群组初始化client
        Client client = sdk.getClient();


// 初始化ProxySignTransactionManager
        ProxySignTransactionManager proxySignTransactionManager;
// ProxySignTransactionManager可以接受AsyncTransactionSignercInterface的实现作为构造函数参数
        proxySignTransactionManager = new ProxySignTransactionManager(client, (hash, transactionSignCallback) -> {
            SignatureResult sign = client.getCryptoSuite().sign(hash, AccountManger.loadAccount(hexPrivateKey));
            transactionSignCallback.handleSignedTransaction(sign);
        });

        ContractCodec contractCodec=new ContractCodec(client.getCryptoSuite(),false);

        List<Object>params=new ArrayList<>();
        params.add(pet.getId());
        params.add(pet.getName());
        params.add(pet.getType());
        params.add(pet.getSellerId());
        params.add(pet.getPetPrice());
        params.add(pet.getStatus());
        params.add(pet.getImgUrl());
        params.add(pet.getIsDeleted());

// 对合约参数进行编解码
        byte[] abiEncoded = contractCodec.encodeMethod(PetContract.getABI(), "update", params);

// 采用链式构造AbiEncodedRequest，传入contractAddress、nonce、blockLimit等重要参数，最后使用buildAbiEncodedRequest结束构造。
        AbiEncodedRequest request =
                new TransactionRequestBuilder()
                        .setTo(CommonClient.getContractAddress("PetMarket"))
                        .setAbi(PetContract.getABI())
                        .setBlockLimit(BigInteger.valueOf(2000))
                        .setExtension("Hello World".getBytes())
                        .setGasPrice(BigInteger.TEN)
                        .buildAbiEncodedRequest(abiEncoded);

// 同步发送上链，获得回执
        TransactionReceipt receipt = proxySignTransactionManager.sendTransaction(request);

        logger.info("调用petClient的edit(重载)方法");
        logger.info("结果：{}", receipt);
    }

    public void remove(Pet pet) throws ContractException {
        PetContract petContract = (PetContract) getContractMap().get("PetMarket");
        this.query(pet);
        TransactionReceipt receipt = petContract.remove(pet.getId());
        logger.info("调用petClient的remove方法");
        logger.info("结果：{}", receipt);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        BcosSDK sdk = SpringUtils.getBean("bcosSDK");
        deploy("PetMarket", PetContract.class, sdk);
    }



    public PageResult<Pet> getPetPage(Integer pageNo, Integer pageSize) throws ContractException {
        PageResult<Pet> pageResult = new PageResult<>();
        PetContract petContract = (PetContract) getContractMap().get("PetMarket");
        BigInteger start = BigInteger.valueOf((pageNo - 1) * pageSize);
        BigInteger end = BigInteger.valueOf(pageNo * pageSize);
        System.out.println("start:" + start + "end:" + end);
        List<Pet> petList = new ArrayList<>();
        int count = 0,begin = start.intValue() + 1,last = end.intValue();
        while(count < pageSize){
            Pet pet = this.queryInner(BigInteger.valueOf(begin));
            if(pet.getIsDeleted().equals("1")){
                begin++;
                continue;
            }
            else if(pet.getIsDeleted().equals("")){
                break;
            }
            petList.add(pet);
            count++;
            begin++;
        }
        pageResult.setRecords(petList);
        return pageResult;
    }


    public int countPet() throws ContractException {
        int ans=0;
        int cur=1;
        while(true){
            Pet pet = this.queryInner(BigInteger.valueOf(cur));

            if(pet.getIsDeleted().equals("1")){
                //ans++;
                cur++;
                continue;
            }
            else if(pet.getIsDeleted().equals("")){
                break;
            }

            ans++;
            cur++;

        }

        return ans;

    }

    private Pet queryInner(BigInteger id) throws ContractException {
        PetContract petContract = (PetContract) getContractMap().get("PetMarket");
        Tuple7<String, String, String, String, String, String, String> getValue = petContract.select(id);
        Pet pet = new Pet();
        pet.setId(id);
        pet.setName(getValue.getValue1());
        pet.setType(getValue.getValue2());
        pet.setSellerId(getValue.getValue3());
        pet.setPetPrice(getValue.getValue4());
        pet.setStatus(getValue.getValue5());
        pet.setImgUrl(getValue.getValue6());
        pet.setIsDeleted(getValue.getValue7());
        return pet;
    }
}
